Coverage Report

Created: 2024-12-19 06:34

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
D:\a\tools.proto\tools.proto\compiler\src\api\core\generator.rs
Line
Count
Source
1
// Copyright (c) 2024, BlockProject 3D
2
//
3
// All rights reserved.
4
//
5
// Redistribution and use in source and binary forms, with or without modification,
6
// are permitted provided that the following conditions are met:
7
//
8
//     * Redistributions of source code must retain the above copyright notice,
9
//       this list of conditions and the following disclaimer.
10
//     * Redistributions in binary form must reproduce the above copyright notice,
11
//       this list of conditions and the following disclaimer in the documentation
12
//       and/or other materials provided with the distribution.
13
//     * Neither the name of BlockProject 3D nor the names of its contributors
14
//       may be used to endorse or promote products derived from this software
15
//       without specific prior written permission.
16
//
17
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
21
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29
use crate::api::core::Error;
30
use crate::compiler;
31
use crate::compiler::util::imports::{ImportSolver, ProtocolStore};
32
use crate::gen::codec::CodecMap;
33
use crate::gen::file::FileType;
34
use bp3d_debug::trace;
35
use bp3d_util::index_map::IndexMap;
36
use bp3d_util::path::PathExt;
37
use std::marker::PhantomData;
38
use std::path::{Path, PathBuf};
39
40
pub struct Context<'a, I: ImportSolver> {
41
    items: IndexMap<Item<'a>>,
42
    pub protocols: &'a ProtocolStore<'a, I>,
43
}
44
45
impl<'a, I: ImportSolver> Context<'a, I> {
46
2
    pub fn new(protocols: &'a ProtocolStore<'a, I>) -> Self {
47
2
        Self {
48
2
            items: IndexMap::with_capacity(protocols.len()),
49
2
            protocols,
50
2
        }
51
2
    }
52
53
10
    pub fn generate<G: crate::gen::Generator>(
54
10
        &mut self,
55
10
        generator: &Generator<G>,
56
10
        full_name: impl AsRef<str>,
57
10
        params: &Params,
58
10
        generator_params: &G::Params<'_>,
59
10
    ) -> Result<(), Error> {
60
10
        if self.get(full_name.as_ref()).is_none() {
  Branch (60:12): [True: 10, False: 0]
  Branch (60:12): [True: 0, False: 0]
  Branch (60:12): [Folded - Ignored]
61
10
            let protocol = self
62
10
                .protocols
63
10
                .get(full_name.as_ref())
64
10
                .ok_or_else(|| 
Error::ProtocolNotFound(full_name.as_ref().into())0
)
?0
;
65
10
            self.items.insert(generator.generate(protocol, params, generator_params)
?0
);
66
0
        }
67
10
        Ok(())
68
10
    }
69
70
2
    pub fn generate_all<G: crate::gen::Generator>(
71
2
        &mut self,
72
2
        generator: &Generator<G>,
73
2
        params: &Params,
74
2
        generator_params: &G::Params<'_>,
75
2
    ) -> Result<(), Error> {
76
35
        for protocol in 
self.protocols.iter()2
{
77
35
            if self.get(&protocol.full_name).is_none() {
  Branch (77:16): [True: 25, False: 10]
  Branch (77:16): [True: 0, False: 0]
  Branch (77:16): [Folded - Ignored]
78
25
                self.items.insert(generator.generate(protocol, params, generator_params)
?0
);
79
10
            }
80
        }
81
2
        Ok(())
82
2
    }
83
84
45
    pub fn get(&self, full_name: &str) -> Option<&Item<'a>> {
85
45
        self.items.get(full_name)
86
45
    }
87
88
2
    pub fn iter(&self) -> impl Iterator<Item = &Item<'a>> {
89
2
        self.items.iter()
90
2
    }
91
}
92
93
pub struct Item<'a> {
94
    full_name: &'a str,
95
    pub name: &'a str,
96
    pub path: PathBuf,
97
}
98
99
impl bp3d_util::index_map::Index for Item<'_> {
100
    type Key = str;
101
102
45
    fn index(&self) -> &Self::Key {
103
45
        self.full_name
104
45
    }
105
}
106
107
#[derive(Copy, Clone, Debug)]
108
pub struct Params {
109
    pub write_messages: bool,
110
    pub read_messages: bool,
111
    pub use_enums: bool,
112
    pub use_structs: bool,
113
    pub use_messages: bool,
114
    pub use_unions: bool,
115
}
116
117
impl Default for Params {
118
12
    fn default() -> Self {
119
12
        Self {
120
12
            write_messages: true,
121
12
            read_messages: true,
122
12
            use_enums: true,
123
12
            use_structs: true,
124
12
            use_messages: true,
125
12
            use_unions: true,
126
12
        }
127
12
    }
128
}
129
130
impl Params {
131
0
    pub fn new() -> Self {
132
0
        Self {
133
0
            write_messages: false,
134
0
            read_messages: false,
135
0
            use_enums: false,
136
0
            use_structs: false,
137
0
            use_messages: false,
138
0
            use_unions: false,
139
0
        }
140
0
    }
141
}
142
143
pub struct Generator<'a, G> {
144
    generator: PhantomData<G>,
145
    out_directory: &'a Path,
146
    file_header: Option<&'a Path>,
147
    codec_map: CodecMap<'a, 'a>,
148
}
149
150
impl<'a, G: crate::gen::Generator> Generator<'a, G> {
151
2
    pub fn new(out_directory: &'a Path, codecs: CodecMap<'a, 'a>, _: G) -> Self {
152
2
        Self {
153
2
            out_directory,
154
2
            generator: PhantomData,
155
2
            file_header: None,
156
2
            codec_map: codecs,
157
2
        }
158
2
    }
159
160
0
    pub fn with_default_codecs(out_directory: &'a Path, _: G) -> Self {
161
0
        Self {
162
0
            out_directory,
163
0
            generator: PhantomData,
164
0
            file_header: None,
165
0
            codec_map: G::get_default_codecs()
166
0
        }
167
0
    }
168
169
0
    pub fn set_file_header(&mut self, path: &'a Path) -> &mut Self {
170
0
        self.file_header = Some(path);
171
0
        self
172
0
    }
173
174
35
    pub fn generate<'b>(
175
35
        &self,
176
35
        protocol: &'b compiler::Protocol,
177
35
        params: &Params,
178
35
        generator_params: &G::Params<'_>,
179
35
    ) -> Result<Item<'b>, Error> {
180
35
        trace!({?params}, "Generating protocol {}", protocol.full_name);
181
35
        let file_header = self
182
35
            .file_header
183
35
            .map(std::fs::read_to_string)
184
35
            .transpose()
185
35
            .map_err(Error::Io)
?0
186
35
            .map(|v| 
G::generate_file_header(v.lines())0
);
187
35
        let name = protocol.name();
188
35
        let files =
189
35
            G::generate(protocol, &self.codec_map, generator_params).map_err(|e| 
Error::Generator(e.to_string())0
)
?0
;
190
35
        let out_path = self.out_directory.join(name);
191
35
        if !out_path.exists() {
  Branch (191:12): [True: 35, False: 0]
  Branch (191:12): [True: 0, False: 0]
  Branch (191:12): [Folded - Ignored]
192
35
            std::fs::create_dir(&out_path).map_err(Error::Io)
?0
;
193
0
        }
194
211
        let 
files_iter = files.into_iter().filter(35
|v| {
195
211
            let value = match v.ty() {
196
35
                FileType::MessageWriting => params.write_messages,
197
36
                FileType::MessageReading => params.read_messages,
198
35
                FileType::Message => params.use_messages,
199
35
                FileType::Structure => params.use_structs,
200
35
                FileType::Enum => params.use_enums,
201
35
                FileType::Union => params.use_unions,
202
            };
203
211
            if !value {
  Branch (203:16): [True: 2, False: 209]
  Branch (203:16): [True: 0, False: 0]
  Branch (203:16): [Folded - Ignored]
204
2
                trace!("Excluding {:?} from generation", v);
205
209
            } else {
206
209
                trace!("Including {:?} in generation", v);
207
209
            }
208
211
            value
209
211
        }
)35
;
210
35
        let iter = files_iter
211
35
            .into_iter()
212
209
            .map(|v| v.write(&out_path, file_header.as_deref(), G::get_language_extension()))
213
209
            .filter_map(|v| match v {
214
209
                Ok(o) => o.map(Ok),
215
0
                Err(e) => Some(Err(e)),
216
209
            })
217
35
            .collect::<std::io::Result<Vec<PathBuf>>>()
218
35
            .map_err(Error::Io)
?0
;
219
89
        let 
umbrella35
=
G::generate_umbrella(name, iter.iter().map(35
|v| &**v), generator_params)
220
35
            .map_err(|e| 
Error::Generator(e.to_string())0
)
?0
;
221
35
        let proto_path = if umbrella.len() > 1 {
  Branch (221:29): [True: 35, False: 0]
  Branch (221:29): [True: 0, False: 0]
  Branch (221:29): [Folded - Ignored]
222
35
            let umbrella_path = out_path.join("umbrella").ensure_extension(G::get_language_extension()).to_path_buf();
223
35
            std::fs::write(&umbrella_path, umbrella).map_err(Error::Io)
?0
;
224
35
            umbrella_path
225
        } else {
226
0
            out_path
227
        };
228
35
        Ok(Item {
229
35
            full_name: &protocol.full_name,
230
35
            name,
231
35
            path: proto_path,
232
35
        })
233
35
    }
234
}